package com.pan.framework.core.aop;

import com.pan.framework.anno.aop.MyAfter;
import com.pan.framework.anno.aop.MyBefore;
import com.pan.framework.core.aop.entiy.MyCutArgs;
import com.pan.framework.proxy.MyInvocationHandler;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyAopInvocationHandler implements MyInvocationHandler {

    private Object targetBean;

    private Method targetMethod;
    private Object aspect;

    private Class[] paramTypeClasses;//targetMethod ParamType

    private Class[] returnTypeClass;
    private Method aspectMethod;

    public MyAopInvocationHandler(Method targetMethod, Object targetBean, Object aspect, Method aspectMethod) {
        this.targetMethod = targetMethod;
        this.targetBean = targetBean;
        this.paramTypeClasses = targetMethod.getParameterTypes();
        returnTypeClass = new Class[]{targetMethod.getReturnType()};
        this.aspect = aspect;
        this.aspectMethod = aspectMethod;
    }

    @Override
    public Object getTargetBean() {
        return targetBean;
    }

    @Override
    public void setTargetBean(Object targetBean) {
        this.targetBean = targetBean;
    }

    public boolean equalsParamTypeClasses(Method a, Method b) {
        Class[] aArr = a.getParameterTypes();
        Class[] bArr = b.getParameterTypes();
        if (aArr.length != bArr.length) {
            return false;
        }
        for (int i = 0; i < aArr.length; i++) {
            if (aArr[i] != bArr[i]) {
                return false;
            }
        }
        return true;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (!targetMethod.getName().equals(method.getName()) ||
                !equalsParamTypeClasses(targetMethod, method)) {
            return method.invoke(targetBean, args);
        }
        Object result = null;
        if (aspectMethod.isAnnotationPresent(MyBefore.class)) {
            MyCutArgs myCutArgs = new MyCutArgs(paramTypeClasses, args);
            if (myCutArgs.getLength() == 0)
                aspectMethod.invoke(aspect);
            else
                aspectMethod.invoke(aspect, myCutArgs);
            result = paramTypeClasses.length ==0?
                    method.invoke(targetBean):
                    method.invoke(targetBean, myCutArgs.getArgs());

        } else if (aspectMethod.isAnnotationPresent(MyAfter.class)) {
            result = method.invoke(targetBean, args);
            MyCutArgs myCutArgs = new MyCutArgs(new Class[]{method.getReturnType()}, new Object[]{result});
            //debug
//            System.out.println("after myCutArgs: " + myCutArgs);
            aspectMethod.invoke(aspect, myCutArgs);
            result = myCutArgs.getArgs()[0];
        } else {
            throw new RuntimeException(aspectMethod.getName() + "方法不存在切面注解");
        }
        return result;
    }
}
